﻿using Common;
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace CarPCBATest.Procedure
{
    /// <summary>
    /// IO模块
    /// </summary>
    public enum DIDefine
    {
        /// <summary>
        /// 安全光幕
        /// </summary>
        SafeLight = 0,

        /// <summary>
        /// 启动
        /// </summary>
        Start,

        /// <summary>
        /// 急停
        /// </summary>
        EmgStop,

        /// <summary>
        /// 复位(空缺)
        /// </summary>
        Reset,

        /// <summary>
        /// 气缸到位
        /// </summary>
        AirWork
    }

    public enum DODefine
    {
        /// <summary>
        /// 产品供电
        /// </summary>
        Power = 0,

        /// <summary>
        /// 测试通过指示
        /// </summary>
        OK,

        /// <summary>
        /// NG蜂鸣器
        /// </summary>
        NG,

        /// <summary>
        /// 气缸复位
        /// </summary>
        AirReset,

        /// <summary>
        /// 高电平烧录低电平测试
        /// </summary>
        LineSwitch
    }

    internal class IOModule
    {
        public static void Dispose()
        {
            disposing = true;
            //client.Close();
        }

        private static Socket client;
        private static readonly object objLock = new object();
        private static bool disposing = false;
        private static bool connected = false;

        [Conditional("DEVICE")]
        public static void Connect()
        {
            if (connected)
                return;
            var ip = "192.168.1.12";
            var port = 502;
            client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建Socket

            //IPEndPoint ipe = new IPEndPoint(IPAddress.Parse(ip), port);//把ip和端口转化为IPEndpoint实例
            try
            {
                client.Connect(IPAddress.Parse(ip), port);
                Task.Run(() =>
                {
                    while (!disposing)
                    {
                        RefreshIOValue();
                    }
                });
                connected = true;
            }
            catch (Exception)
            {
                throw;
            }
        }

        private static void RefreshIOValue()
        {
            ReadDIValue();
            Thread.Sleep(50);

            DiChangeExcute();

            ReadDOValue();
            Thread.Sleep(50);
        }

        private static void DiChangeExcute()
        {
            if (GetDIValue(DIDefine.Start))
            {
                StartAction?.BeginInvoke(null, null);
            }
            if (!GetDIValue(DIDefine.EmgStop))
            {
                EmgStopAction?.Invoke();
            }
            else
            {
                EmgCancelAction?.Invoke();
            }
        }

        private static byte[] cmdReadDOValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x10 };

        private static byte[] cmdReadDIValue = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10 };

        private static void ReadDIValue()
        {
            var data = SendCommand(cmdReadDIValue);
            if (!CheckReceivedData(data, 0x02, new byte[] { 0x00, 0x05 }))
            {
                Mylog.Error($"read di value error, receive[{data}]");
            }
            DIValue = new byte[] { data[9], data[10] };
        }

        public static void LogIOStatus()
        {
            foreach (DIDefine item in Enum.GetValues(typeof(DIDefine)))
            {
                var tmp = GetDIValue(item);
                Mylog.Info($"DIStatus:{item.ToString()}[{tmp}]");
            }
            foreach (DODefine item in Enum.GetValues(typeof(DODefine)))
            {
                var tmp = GetDOValue(item);
                Mylog.Info($"DOStatus:{item.ToString()}[{tmp}]");
            }
        }

        public static void SetDOValue(byte value)
        {
            byte[] cmd = { 0x00, 0x00, 0x00, 0x00,
                0x00, 0x08, 0x01, 0x0f,
                0x00, 0x00, 0x00, 0x08,
                0x01,value };
            var data = SendCommand(cmd);
            if (!CheckReceivedData(data, 0x0f, new byte[] { 0x00, 0x06 }))
            {
                Mylog.Error($"set do value error,received data:[{data}]");
            }
        }

        public static void SetDOValue(DODefine index, int value)
        {
            //Task.Run(() =>
            //{
                int doIndex = (int)index;
                byte[] cmd = { 0x00, 0x00, 0x00, 0x00,
                0x00, 0x06, 0x01, 0x05,
                0x00, Convert.ToByte(doIndex), (value==0)?(byte)0x00:(byte)0xff, 0x00};
                var data = SendCommand(cmd);
                if (!CheckReceivedData(data, 0x05, new byte[] { 0x00, 0x06 }))
                {
                    throw new Exception($"set do value error,received data:[{data}]");
                }
            //});
        }

        public static void ReadDOValue()
        {
            var data = SendCommand(cmdReadDOValue);
            if (!CheckReceivedData(data, 0x01, new byte[] { 0x00, 0x05 }))
            {
                Mylog.Error($"read do value error, receive[{data}]");
            }
            DOValue = new byte[] { data[9], data[10] };
        }

        private static byte[] SendCommand(byte[] cmd)
        {
            lock (objLock)
            {
                client.Send(cmd, cmd.Length, SocketFlags.None);

                byte[] recvBytes = new byte[1024];
                //Thread.Sleep(0);
                int actualBytes = client.Receive(recvBytes, 0, recvBytes.Length, SocketFlags.None);
                return recvBytes;
                //string recvStr = Encoding.GetEncoding("ASCII").GetString(recvBytes, 0, actualBytes);
            }
        }

        private static bool CheckReceivedData(byte[] data, byte funcCode, byte[] dataLen)
        {
            for (int i = 0; i < 4; i++)
            {
                if (data[i] != 0x00)
                {
                    return false;
                }
            }
            for (int i = 0; i < 2; i++)
            {
                if (data[4 + i] != dataLen[i])
                {
                    return false;
                }
            }
            if (data[6] != 0x01)
            {
                return false;
            }
            if (data[7] != funcCode)
            {
                return false;
            }
            return true;
        }

        private static byte[] _diValue;

        public static byte[] DIValue
        {
            get { return _diValue; }
            private set
            {
                _diValue = value;
                //Mylog.Debug($"DIValue [{value}]");
            }
        }

        private static byte[] _doValue;

        public static byte[] DOValue
        {
            get { return _doValue; }
            set
            {
                _doValue = value;
                //Mylog.Debug($"DOValue [{value}]");
            }
        }

        public static bool GetDIValue(DIDefine index)
        {
            return DIValue.Index((int)index);
        }

        public static bool GetDOValue(DODefine index)
        {
            return DOValue.Index((int)index);
        }

        public static string ErrMsg { get; set; }

        public static Action StartAction = null;
        public static Action EmgStopAction = null;
        public static Action EmgCancelAction = null;
        public static Action StopAction = null;
        public static Action ResetAction = null;
    }
}